package org.infinitest.swingui;

import static org.infinitest.EventFakeSupport.*;
import static org.junit.Assert.*;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JTextArea;
import javax.swing.JTree;
import javax.swing.tree.TreePath;

import junit.framework.AssertionFailedError;

import org.infinitest.TestEvent;
import org.infinitest.swingui.TestEventSelectionListener;
import org.junit.Before;
import org.junit.Test;

public class WhenUserClicksOnResultTree
{
  private ResultClickListener clickListener;
  private List<TestEvent> paneEvents;
  private List<TreePath> expandedPaths;
  private KeyListener keyListener;

  @Before
  public void inContext()
  {
    paneEvents = new ArrayList<TestEvent>();
    expandedPaths = new ArrayList<TreePath>();
    TestEventSelectionListener selectionListener = new TestEventSelectionListener()
    {
      public void showInfoPane(TestEvent event)
      {
        paneEvents.add(event);
      }

      public void expandPath(JTree tree, TreePath path)
      {
        tree.expandPath(path);
      }
    };
    keyListener = new EnterPressListener()
    {
      @Override
      protected void showInfoPane(TestEvent event)
      {
        paneEvents.add(event);
      }
    };
    clickListener = new ResultClickListener(selectionListener);
  }

  @Test
  public void shouldPopUpInfoFrameOnEventDoubleClick() throws Exception
  {
    JTree tree = createFakeTree(eventWithError(new AssertionFailedError()));
    simulateClickEvent(tree, 2);
    assertFalse(paneEvents.isEmpty());
  }

  @Test
  public void shouldShowInfoFrameOnTop() throws Exception
  {
    assertTrue(getFrame().isAlwaysOnTop());
  }

  private EventInfoFrame getFrame()
  {
    return new EventInfoFrame(eventWithError(new AssertionFailedError()));
  }

  @Test
  public void shouldNotBeAbleToEditInfoFrame() throws Exception
  {
    JTextArea textArea = getFrame().getTextArea();
    assertFalse(textArea.isEditable());
  }

  @Test
  public void shouldNotPopUpOnSingleClick() throws Exception
  {
    JTree tree = createFakeTree(eventWithError(new AssertionFailedError()));
    simulateClickEvent(tree, 1);
    assertTrue(paneEvents.isEmpty());
  }

  private void simulateClickEvent(JTree tree, int clickCount)
  {
    clickListener.mouseClicked(new MouseEvent(tree, 0, 0, 0, 0, 0, clickCount, false));
  }

  @Test
  public void shouldExpandNodeOnPointOfFailureClick() throws Exception
  {
    JTree tree = createFakeTree("MyPointOfFailure:31");
    simulateClickEvent(tree, 2);
    assertFalse(expandedPaths.isEmpty());
  }

  private void simulateEnterKeyEvent(JTree tree, int keyCode)
  {
    keyListener.keyPressed(new KeyEvent(tree, 0, 0, 0, keyCode, KeyEvent.CHAR_UNDEFINED));
  }

  @Test
  public void shouldPopUpInfoFrameOnTestNodeEnterKey() throws Exception
  {
    JTree tree = createFakeTree(eventWithError(new AssertionFailedError()));
    tree.setSelectionRow(1);
    simulateEnterKeyEvent(tree, KeyEvent.VK_ENTER);
    assertFalse(paneEvents.isEmpty());
  }

  @Test
  public void shouldDoNothingWhenEnterPressedOnPointOfFailureNodes() throws Exception
  {
    JTree tree = createFakeTree("MyPointOfFailure:31");
    simulateEnterKeyEvent(tree, KeyEvent.VK_ENTER);
    assertTrue(paneEvents.isEmpty());
  }

  @Test
  public void shouldUseDefaultBehaviorForOtherKeys() throws Exception
  {
    JTree tree = createFakeTree(eventWithError(new AssertionFailedError()));
    simulateEnterKeyEvent(tree, KeyEvent.VK_LEFT);
    assertTrue(paneEvents.isEmpty());
  }

  private JTree createFakeTree(final Object treeNode)
  {
    return new JTree()
    {
      @Override
      public TreePath getClosestPathForLocation(int x, int y)
      {
        return new TreePath(new Object[] { "root", "parent", treeNode });
      }

      @Override
      public TreePath getSelectionPath()
      {
        return new TreePath(new Object[] { "root", "parent", treeNode });
      }

      @Override
      public void expandPath(TreePath path)
      {
        expandedPaths.add(path);
      }
    };
  }
}
